# COPYRIGHT BLABLA

# README:
# Turn left/right (A,D) each press will change speed

from ogrebase import OgreView, EventListener
from optparse import OptionParser
import ogre.io.OIS as OIS
import ogre.renderer.OGRE as ogre
import ogre.gui.CEGUI as CEGUI
import time
import math
import os
import sys

if os.name == 'nt':
    CEGUI.System.setDefaultXMLParserName('ExpatParser')
else:
    CEGUI.System.setDefaultXMLParserName('TinyXMLParser')

def convertOISMouseButtonToCegui(buttonID):
    if buttonID == 0:
        return CEGUI.LeftButton
    elif buttonID == 1:
        return CEGUI.RightButton
    elif buttonID == 2:
        return CEGUI.MiddleButton
    elif buttonID == 3:
        return CEGUI.X1Button
    else:
        return CEGUI.LeftButton

def cegui_reldim(x):
    return CEGUI.UDim((x),0)

def cegui_vector2(x, y):
    return CEGUI.UVector2(cegui_reldim(x), cegui_reldim(y))

def cegui_color(x):
    return CEGUI.ColourRect(x,x,x,x)

def _PointHack(x, y):
    return CEGUI.Vector2(x, y)
CEGUI.Point = _PointHack

class CeGuiBase(OgreView, EventListener):
    def __init__(self, title="QuickGui...", fullscreen=False, \
            resolution=(1024, 768), resDir='media', \
            renderSystem=''):
        OgreView.__init__(self, title, fullscreen, resolution, resDir, renderSystem)
        EventListener.__init__(self,self.RenderWindow, True, True)
        self.set_window_resolution(resolution[0], resolution[1])
        self.CEGUIRenderer = 0
        self.CEGUISystem = 0
        self._init_cegui()
        self.ConfListItems = []

    def __del__(self):
        EventListener.__del__(self)
        del self.CEGUISystem
        del self.CEGUIRenderer


    def _init_render_window(self):
        OgreView._init_render_window(self)

    def _init_cegui(self):
        self.CEGUIRenderer = CEGUI.OgreCEGUIRenderer(self.RenderWindow,
                ogre.RenderQueueGroupID.RENDER_QUEUE_OVERLAY, False, 3000,
                self.SceneManager)
        self.CEGUISystem = CEGUI.System(self.CEGUIRenderer)
        CEGUI.Logger.getSingleton().loggingLevel = CEGUI.Insane

        CEGUI.SchemeManager.getSingleton().loadScheme("VanillaSkin.scheme")
        if not CEGUI.FontManager.getSingleton().isFontPresent("Commonwealth-10.font"):
            try:
                CEGUI.FontManager.getSingleton().createFont("Commonwealth-10.font")
            except:
                pass

        CEGUI.System.getSingleton().setDefaultMouseCursor("Vanilla-Images", "MouseArrow")
        CEGUI.System.getSingleton().setDefaultFont("Commonwealth-10")

        CEGUI.System.getSingleton().setGUISheet(
            CEGUI.WindowManager.getSingleton().loadWindowLayout("my.layout", False))

    def mouseMoved(self, evt):
        EventListener.mouseMoved(self, evt)
        CEGUI.System.getSingleton().injectMouseMove(evt.get_state().X.rel, evt.get_state().Y.rel)
        if evt.get_state().Z.rel != 0:
            #TODO
            pass
            #self.GuiManager.injectMouseWheelChange(evt.state.Z.rel)

        return True

    def mousePressed(self, evt, id):
        EventListener.mousePressed(self, evt, id)
        CEGUI.System.getSingleton().injectMouseButtonDown(\
                convertOISMouseButtonToCegui(id))
        return True

    def mouseReleased(self, evt, id):
        EventListener.mouseReleased(self, evt, id)
        CEGUI.System.getSingleton().injectMouseButtonUp(\
                convertOISMouseButtonToCegui(id))
        return True

    def keyPressed(self, evt):
        EventListener.keyPressed(self, evt)
        CEGUI.System.getSingleton().injectChar(evt.text)
        CEGUI.System.getSingleton().injectKeyDown(evt.key)
        return True

    def keyReleased(self, evt):
        EventListener.keyReleased(self, evt)
        CEGUI.System.getSingleton().injectKeyUp(evt.key)
        return True
        

class AnimationControlView(object):
    def __init__(self, view):
        self.OgreView = view
        wmgr = CEGUI.WindowManager.getSingleton()
        rt = CEGUI.System.getSingleton().getGUISheet()
        anim = CEGUI.WindowManager.getSingleton().loadWindowLayout("anim.layout", False)
        self.AttachWnd = CEGUI.WindowManager.getSingleton().loadWindowLayout(\
                "attach.layout", False)
        rt.addChildWindow(anim)
        anim.setPosition(cegui_vector2(.0,.0))
        self.AnimListItems = []
        self.BoneListItems = []
        self.AttachedIds = {}
        self.NextId = 0
        self.CurrentAnim = None
        self.Value = 1.0
        self.Paused = False
        self._subscribe_events()

    def set_mesh(self, mesh):
        self.Mesh = mesh
        self.ListAnimations.resetList()
        for i in range(self.Mesh.getMesh().getNumAnimations()):
            self._add_list_element("Vertex: %s" % \
                    self.Mesh.getMesh().getAnimation(i).getName())
        if self.Mesh.getSkeleton() is None:
            return
        for i in range(self.Mesh.getSkeleton().getNumAnimations()):
            self._add_list_element("Morph: %s" % \
                    self.Mesh.getMesh().getSkeleton().getAnimation(i).getName())
        for i in range(self.Mesh.getSkeleton().getNumBones()):
            self._add_bone_name("%s" % self.Mesh.getSkeleton().getBone(i).\
                    getName())
    
    def _add_bone_name(self, text):
        item = CEGUI.ListboxTextItem(text)
        item.AutoDeleted = False
        item.setSelectionBrushImage("Vanilla-Images", "GenericBrush")
        if self.ListBones.getItemCount() == 0:
            item.setSelected(True)
        else:
            item.setSelected(False)
        self.ListBones.addItem(item)
        self.BoneListItems.append(item)


    def _add_list_element(self, text):
        item = CEGUI.ListboxTextItem(text)
        item.AutoDeleted = False
        item.setSelectionBrushImage("Vanilla-Images", "GenericBrush")
        if self.ListAnimations.getItemCount() == 0:
            item.setSelected(True)
        else:
            item.setSelected(False)
        self.ListAnimations.addItem(item)
        self.AnimListItems.append(item)


    def _subscribe_events(self):
        wmgr = CEGUI.WindowManager.getSingleton()
        playBtn = wmgr.getWindow('AnimWnd/Play')
        pauseBtn = wmgr.getWindow('AnimWnd/Pause')
        stopBtn = wmgr.getWindow('AnimWnd/Stop')
        slider = wmgr.getWindow('AnimWnd/SpeedSlider')
        self.SliderValue = wmgr.getWindow('AnimWnd/SpeedValue')
        self.ListAnimations = wmgr.getWindow('AnimWnd/AnimList')
        attachBtn = wmgr.getWindow('AnimWnd/AttachBtn')

        slider.setCurrentValue(1.0)
        playBtn.subscribeEvent(CEGUI.PushButton.EventClicked, self, "_play_btn")
        pauseBtn.subscribeEvent(CEGUI.PushButton.EventClicked, self, \
                "_pause_btn")
        stopBtn.subscribeEvent(CEGUI.PushButton.EventClicked, self, "_stop_btn")

        slider.subscribeEvent(CEGUI.Slider.EventValueChanged, self, \
                "_speed_slider_change")
        attachBtn.subscribeEvent(CEGUI.PushButton.EventClicked, self, \
                "_attach_btn")

        # attach window
        okBtn = wmgr.getWindow('AttachWnd/OkBtn')
        cancelBtn = wmgr.getWindow('AttachWnd/CancelBtn')
        self.ListBones = wmgr.getWindow('AttachWnd/BonesLst')
        self.AttachMeshName = wmgr.getWindow('AttachWnd/MeshEditBox')
        self.AttachOffsetX = wmgr.getWindow('AttachWnd/XEditBox')
        self.AttachOffsetY = wmgr.getWindow('AttachWnd/YEditBox')
        self.AttachOffsetZ = wmgr.getWindow('AttachWnd/ZEditBox')

        okBtn.subscribeEvent(CEGUI.PushButton.EventClicked, self, \
                "_attach_ok_btn")
        cancelBtn.subscribeEvent(CEGUI.PushButton.EventClicked, self, \
                "_attach_cancel_btn")


    def _attach_btn(self, args):
        CEGUI.System.getSingleton().getGUISheet().addChildWindow(self.AttachWnd)
        self.AttachWnd.setPosition(cegui_vector2(.0,.0))

    def _attach_ok_btn(self, args):
        CEGUI.System.getSingleton().getGUISheet().removeChildWindow(\
                self.AttachWnd)
        #TODO
        item = self.ListBones.getFirstSelectedItem()
        bone = str(item.Text)
        mesh = str(self.AttachMeshName.Text)
        posX = str(self.AttachOffsetX)
        posY = str(self.AttachOffsetY)
        posZ = str(self.AttachOffsetZ)
        mesh = self.OgreView.SceneManager.createEntity('AID%s' % \
                self.NextId, mesh)
        self.AttachedIds[self.NextId] = (mesh, bone)
        self.NextId += 1
        self.Mesh.attachObjectToBone(bone, mesh)



    def _attach_cancel_btn(self, args):
        CEGUI.System.getSingleton().getGUISheet().removeChildWindow(\
                self.AttachWnd)

    def _speed_slider_change(self, args):
        self.Value = args.window.getCurrentValue()
        self.SliderValue.setText("%.1f" % self.Value)
        if self.CurrentAnim is not None and self.CurrentAnim.getEnabled() \
                and not self.Paused:
            self.OgreView.add_animation_state(self.CurrentAnim, self.Value)


    def _play_btn(self, args):
        if self.CurrentAnim is not None:
            self.OgreView.remove_animation_state(self.CurrentAnim)
            self.CurrentAnim.setEnabled(False)
        item = self.ListAnimations.getFirstSelectedItem()
        text = str(item.Text)
        offset = text.find(': ')
        self.CurrentAnim = self.Mesh.getAnimationState(text[offset+2:])
        self.CurrentAnim.setEnabled(True)
        self.CurrentAnim.setLoop(True)
        self.OgreView.add_animation_state(self.CurrentAnim, self.Value)
        self.Paused = False


    def _pause_btn(self, args):
        if self.CurrentAnim is not None:
            if self.Paused:
                self.OgreView.add_animation_state(self.CurrentAnim, self.Value)
                self.Paused = False
            else:
                self.OgreView.remove_animation_state(self.CurrentAnim)
                self.Paused = True


    def _stop_btn(self, args):
        if self.CurrentAnim is not None:
            if not self.Paused:
                self.OgreView.remove_animation_state(self.CurrentAnim)
            self.Paused = False
            self.CurrentAnim.setTimePosition(0)
            self.CurrentAnim.setEnabled(False)
            self.CurrentAnim = None


class AnimationView(object):
    def __init__(self, meshname, distance):
        self.OgreView = CeGuiBase()
        self.QuitApplication = False
        self.MeshName = meshname
        self.Distance = distance
        self.AnimationControlView = AnimationControlView(self.OgreView)

        self.OgreView.register_key_event(OIS.KC_ESCAPE, self._quit)
        self.OgreView.register_key_event(OIS.KC_A, self._left)
        self.OgreView.register_key_event(OIS.KC_D, self._right)
        self.Angle = 0
        self.Turn = 0.01

    def __del__(self):
        if self.OgreView is not None:
            self.deinit()

    def deinit(self):
        del self.AnimationControlView
        del self.OgreView
        self.OgreView = None

    def run(self):
        self._init_scene()

        while not self.QuitApplication:
            delta = self.OgreView.render()
            self.OgreView.update()
            self.set_cam_pos(self.Angle)
            time.sleep(0.01)
            self.Angle += self.Turn

    def _init_scene(self):
        self.OgreView.SceneManager.setAmbientLight(\
                ogre.ColourValue(0.7, 0.7, 0.7))
        self.OgreView.SceneManager.setSkyDome(True, 'Examples/CloudySky', 4,8)
        self.Light = self.OgreView.SceneManager.createLight('mainlight')
        self.Light.setPosition(ogre.Vector3(20, 80, 50))

        self.RootNode = self.OgreView.SceneManager.getRootSceneNode()

        self.Ogre = self.OgreView.SceneManager.createEntity('Oge', \
                self.MeshName)
        self.NodeOgre = self.RootNode.createChildSceneNode('nodeOgre')
        self.NodeOgre.setPosition(ogre.Vector3(0,0,0))
        self.NodeOgre.attachObject(self.Ogre)

        self.set_cam_pos(self.Angle)
        self.AnimationControlView.set_mesh(self.Ogre)

    def set_cam_pos(self, angle):
        self.OgreView.Camera.setPosition(ogre.Vector3(math.cos(angle) * \
                self.Distance,\
                self.Distance/5, math.sin(angle) * self.Distance))
        self.OgreView.Camera.lookAt(ogre.Vector3(0, 0, 0))
       

    def _quit(self, pressed):
        if not pressed:
            self.QuitApplication = True

    def _left(self, pressed):
        if pressed:
            self.Turn += 0.01

    def _right(self, pressed):
        if pressed:
            self.Turn -= 0.01

def parseCmd():
    parser = OptionParser()
    parser.add_option("-f", "--file", dest="filename", \
            help="Name of mesh file to load", default="druid.mesh")
    parser.add_option("-d", "--distance", dest="distance", \
            help="The distance of the camera", type="float", default=40.0)

    (options, args) = parser.parse_args()
    return options

if __name__ == '__main__':
    try:
        result = parseCmd()
        t = AnimationView(result.filename, result.distance)
        t.run()
        t.deinit()
    except ogre.OgreException, e:
        print e

